#include "environment.hpp"
#include "lexer.hpp"
#include "parser.hpp"

#include <fstream>
#include <cmath>

Environment::Environment(void) : scope(1)
{
	map<string, Variable> blank;
	_var.push_back(blank);
	flags = No_Env_Flags;
	
	_var[0]["__scope__"] = scope;
	_var[0]["__return__"] = scope;
	_var[0]["__interactive__"] = false;
	_var[0]["__precision__"] = 6;
	_var[0]["__arrprint_start__"] = (Variable)"";
	_var[0]["__arrprint_end__"] = (Variable)"";
	_var[0]["__iosep__"] = (Variable)"";
	_var[0]["__prompt__"] = (Variable)">>";
	_var[0]["__delimiter__"] = (Variable)"\n";
	_var[0]["__epsilon__"] = (Variable)(__DBL_EPSILON__ * 4);
	
	vector<Variable> vec;
	vec.push_back(0);
	// There's no way to calculate this one in Oratr...not quickly
	vec[0] = M_E;
	repl["E"] = vec;
	vec[0] = false;
	repl["false"] = vec;
	vec[0] = true;
	repl["true"] = vec;
	vec[0] = (Variable)"\t";
	repl["__tab__"] = vec;
#if defined(USING_WINDOWS)
	vec[0] = (Variable)"\r\n";
#elif defined(USING_LINUX)
	vec[0] = (Variable)"\n";
#endif
	repl["__endl__"] = vec;
	
	vec[0] = CURRENT_OS;
	vec[0].type = tString;
	switch(CURRENT_OS) {
	case OS_windows:
		vec[0].str = "Windows";
		break;
	case OS_apple:
		vec[0].str = "Apple";
		break;
	case OS_linux:
		vec[0].str = "Linux";
		break;
	case OS_nds:
		vec[0].str = "Nintendo DS";
		break;
	default:
		vec[0].str = "Unidentified";
		break;
	}
	repl["CURRENT_OS"] = vec;
}

void Environment::inscope(void)
{
	scope++;
	_var[0]["__scope__"] = scope;
	_var.push_back(map<string,Variable>());
}

void Environment::outscope(void)
{
	if(scope>1) {
		scope--;
		_var.pop_back();
		_var[0]["__scope__"] = scope;
	}
}

Environment* Environment::add(string s, Function f)
{
	if( _func.find(s) == _func.end() ) {
		_func[s]=f;
	}
	return this;
}

Variable& Environment::getvar(string fullkey)
{
	string key="";
	vector<pair<Variable,bool> > offsets; // the bool means whether it's a typecast or an expression
	Variable* ret = NULL;
	if(fullkey.find_first_of(":$") != string::npos) {
		// It detects an "offset"
		size_t loc = fullkey.find_first_of(":$");
		key = fullkey.substr(0,loc);
		do {
			loc = fullkey.find_first_of(":$");
			char type = fullkey[loc];
			fullkey = fullkey.substr(loc+1);
			Variable var = fullkey.substr(0,fullkey.find_first_of(":$"));
			Variable tmp = var.i;
			if(tmp.str != var.str) {
				// It's a varname
				var.type = tVarname;
				offsets.push_back(make_pair(var,type=='$'));
			} else {
				// It's numeric
				offsets.push_back(make_pair(tmp,false));
			}
		} while(fullkey.find_first_of(":$") != string::npos);
	} else {
		// It's the full string
		key = fullkey;
	}
	for(int i=_var.size()-1;i>=0;i--) {
		if( _var[i].find(key) != _var[i].end() ) {
			ret = &_var[i][key];
			break;
		}
	}
	if(ret == NULL) {
		ret = &_var.back()[key];
	}
	for(unsigned i=0;i<offsets.size();i++) {
		if(offsets[i].second) {
			// Force a typecast of the current type
			vector<Variable> tmp;
			tmp.push_back((Variable)"new");
			tmp.push_back(offsets[i].first);
			tmp.push_back(*ret);
			parse(tmp,*this);
			*ret = this->getvar("__return__");
		} else {
			if(ret->type == tArray && offsets[i].first.type == tNumeric) {
				// Go to each offset
				verify(offsets[i].first);
				if((ret->arr.size() <= (unsigned)offsets[i].first.i) || ((int)offsets[i].first.i < 0)) {
					throw_error(ERROR_ARRAYBOUNDS);
					return *ret;
				}
				ret = &ret->arr[(unsigned)offsets[i].first.i];
			} else if(ret->type == tCustomType && offsets[i].first.type == tVarname) {
				unsigned off = types[ret->str].get(offsets[i].first.str);
				ret = &ret->arr[off];
			} else {
				throw_error(ERROR_ARGUMENTS);
			}
		}
	}
	return *ret;
}

Variable& Environment::verify(Variable& v)
{
	if(v.type == tVarname) {
		v = getvar(v.str);
	}
	return v;
}

string findfile(Environment& env, string filename)
{
	fstream file;
	string ret;
	if(file.is_open()) {
		file.close();
	}
	if(filename.size() > 3) {
		if(filename.substr(filename.size()-3,3)!=FILEXT) {
			filename += FILEXT;
		}
	} else {
		filename += FILEXT;
	}
	string path="";
	for(unsigned i=0;i<env._paths.size();i++) {
		path = env._paths[i];
		if(path[path.size()-1]!=PATHSEPARATOR) {
			path += PATHSEPARATOR;
		}
		path += filename;
		file.open(path.c_str(), ios_base::in);
		if(file.is_open()) {
			ret = path;
			file.close();
			break;
		} else {
			file.close();
		}
	}
	return ret;
}

bool isAbsolutePath(string str)
{
	if(CURRENT_OS == OS_windows) {
		if(str.size() < 3) {
			return false;
		}
		return(isalpha(str[0]) &&
			str[1]==':' &&
			str[2]==PATHSEPARATOR
		);
	} else if(CURRENT_OS == OS_nds) {
		if(str.size() < 7) {
			return false;
		}
		return(str.substr(0,5)=="fat://");
	} else {
		if(str.size() == 0) {
			return false;
		}
		return(str[0]==PATHSEPARATOR);
	}
}
